/*******************************************************************/
/*    Copyright (c) 1999-2020 by Spatial Corp.                     */
/*    All rights reserved.                                         */
/*    Protected by U.S. Patents 5,257,205; 5,351,196; 6,369,815;   */
/*                              5,982,378; 6,462,738; 6,941,251    */
/*    Protected by European Patents 0503642; 69220263.3            */
/*    Protected by Hong Kong Patent 1008101A                       */
/*******************************************************************/

//----------------------------- IopAcisPS.cpp -------------------------------

#include <iostream>
#include <fstream>
#include <vector>
#include <time.h>
#include <cmath> // fabs

//Acis
#include "lists.hxx"
#include "kernapi.hxx" 

//Iop
#include "SPAIop.h"
#include "SPAIopAcis.h"
#include "SPAIopPolicyOptions.h"
#include "SPAIopArgParser.h"
#include "SPAIopSampleUtilities.h"
#include "SPAIopAcisSampleUtilities.h"
#include "SPAIopSampleResultUtils.h"
#include "SPAIopOccurrenceUtils.h"
#include "SPAIopPerformanceStatistics.h"
#include "SPAIopDocumentDataImporter.h"
#include "SPAIopDocumentDataImportResult.h"
// License file
#include "spatial_license.h"
using namespace SPAIopSampleLogging;
using namespace SPAIopAcisPrintUtils;

class ProgressReporter;
ProgressReporter* _g_pProgressReporter = nullptr;

InstancePathsPIDOccurrenceMap instPathPIDOccurrenceMap;

class ProgressReporter
{
public:

	//-----------------------------------------------------------------------------------------

	ProgressReporter() : _psDone(false), _numParts(0), _partsDone(0),
		_lastProgress(-1) {}

	//-----------------------------------------------------------------------------------------

	void ProgressCallBack(double iProgress, bool& iAbort)
	{
		int progressRange = 100;
		int percent = 0;

		double actualProgressPercent = iProgress * 100;
		int progress = static_cast<int>(actualProgressPercent);

		if (!_psDone)
		{
			percent = (int)((100.0 * progress) / double(progressRange));
			PrintMessage(L"Product Structure Progress : %d%%\n", percent);
		}
	}

	//-----------------------------------------------------------------------------------------

	void SetNumParts(int iNumParts)
	{
		_numParts = iNumParts;
	}


	//-----------------------------------------------------------------------------------------

	void SetPsDone(bool iIsDone)
	{
		_psDone = iIsDone;
	}

	//-----------------------------------------------------------------------------------------


private:

	//-----------------------------------------------------------------------------------------

	bool _psDone;

	size_t _numParts;
	size_t _partsDone;
	int _lastProgress;

	//-----------------------------------------------------------------------------------------

};

//-----------------------------------------------------------------------------------------

void MyProgressCallBack(double iProgress, bool& iAbort)
{
	if (nullptr != _g_pProgressReporter)
	{
		_g_pProgressReporter->ProgressCallBack(iProgress, iAbort);
	}
}
//-----------------------------------------------------------------------------------------

bool ImportProductStructure(SPAIopDocument& ipSourceDoc, const SPAIopPolicy& iPolicy, SPAIopInputProductStructure& oInputPS, SPAIopPSImportResult& oInputPSResult);
int ImportPSModel(const wchar_t*& ipSourceFilePath, const SPAIopPolicy& iPolicy);

void PrintHelp()
{
	PrintMessage(L"Usage: IopAcisPS -i <full-input-file-path> -ipsinfo <full-output-file-path> [-p <full-policy-file-path>]  \n");
	PrintMessage(L"\t-i	: The absolute path to the input file. \n");
	PrintMessage(L"\t-ipsinfo	: The absolute path to the output txt file. \n");
	PrintMessage(L"\t-p	: The absolute path to the policy file used to control optional behavior during operations. \n");
	PrintMessage(L"\t-h	: This help.\n");
}

//-----------------------------------------------------------------------------------------

int ImportPSModel(const wchar_t*& ipSourceFilePath, const SPAIopPolicy& iPolicy)
{
	int returnValue = SPAIopSampleResult_Import_Ok;

	_g_pProgressReporter = new ProgressReporter();
	// Import Input Product Structure 
	SPAIopInputProductStructure inputPS;
	SPAIopPSImportResult inputPSResult;

	// Create Source Document from input file
	std::wstring sourceFilePath(ipSourceFilePath);
	SPAIopWString wSourceFilePath(sourceFilePath.c_str());
	SPAIopDocument sourceDoc(wSourceFilePath);

	bool gotPS = ImportProductStructure(sourceDoc, iPolicy, inputPS, inputPSResult);

	if (gotPS)
	{
		ImportAssemblyInstanceData(inputPS);
		SPAIopDocumentDataImporter psDataImporter;
		SPAIopDocumentDataImportResult rootPSDataResult = psDataImporter.ImportWithPolicy(iPolicy, sourceDoc);
		ImportAssemblyOccurrenceData(inputPS, instPathPIDOccurrenceMap, iPolicy, rootPSDataResult);
	}
	else
		returnValue = SPAIopSampleResult_Import_PS_Fail;
	delete _g_pProgressReporter;
	_g_pProgressReporter = NULL;
	return returnValue;
}

//================================================================================================================================================================================

bool ImportProductStructure(SPAIopDocument& ipSourceDoc, const SPAIopPolicy& iPolicy, SPAIopInputProductStructure& oInputPS, SPAIopPSImportResult& oInputPSResult)
{
	bool returnValue = true;
	// Import the Assembly tree as a Product Structure.  Note that 
	// importing a Product Structure does not import any brep data associated
	// with the model, which is done separately from part-associated reference nodes
	// in the Product Structure.  Use try/catch exception handling blocks to 
	// manage fatal errors.


	// Create a Product Strcuture Importer
	SPAIopProductStructureImporter psImporter;

	try
	{
		psImporter.SetProgressCallBack((SPAIopProgressCallback)MyProgressCallBack);

		oInputPSResult = psImporter.ImportWithPolicy(iPolicy, ipSourceDoc, oInputPS);
		SPAIopLengthUnitType inputPSUnit = oInputPS.GetUnits();
		if (SPAIopLengthUnit_Unknown != inputPSUnit)
		{
			SPAIopWString unitStr = SPAIopLengthUnitUtils::LengthUnitToString(inputPSUnit);
			const wchar_t* pUnitArray = NULL;
			unitStr.ToWChar(pUnitArray);
			std::wcout << "Product Structure Units: " << pUnitArray << std::endl;
		}

		if (_g_pProgressReporter != NULL)
		{
			_g_pProgressReporter->SetNumParts(oInputPS.GetUniqueReferencesCount());
			_g_pProgressReporter->SetPsDone(true);
		}
	}
	catch (SPAIopAcisError& error)
	{
		DisplayError(error);
		returnValue = false;
	}
	catch (...)
	{
		std::wcout << "FAILURE: Unknown exception caught! " << std::endl;
		returnValue = false;
	}

	return returnValue;
}

//===========================================================================================================================================================================

int test_IopAcisPS(int argc, char* argv[])
{
	
	int result = 1;
	SPAIopPerformanceStatistics printPerfoStatsObj;

	api_start_modeller(0);

	try
	{


		char* argFields[] = { "i", "ipsinfo", "p" };
		int size = sizeof(argFields) / sizeof(*argFields);
		SPAIopArgParser parser(size, argFields, argc, argv, &PrintHelp);

		SPAIopAcisSystemInitGuard initGuard(SPATIAL_LICENSE);

		result = initGuard.GetInitStatus();

		if (true == parser.IsSuccess())
		{

			const wchar_t* pSourceFilePath = NULL;
			bool hasInputFileName = parser.GetWStringValueFor("i", pSourceFilePath); // GetStringValueFor


			const wchar_t* pTargetFilePath = NULL;
			bool hasOutputFileName = parser.GetWStringValueFor("ipsinfo", pTargetFilePath);

			const wchar_t* pPolicyFilePath = NULL;
			bool hasPolicyFileName = parser.GetWStringValueFor("p", pPolicyFilePath);

			if (hasInputFileName && hasOutputFileName && pSourceFilePath && pTargetFilePath)
			{
				SPAIopPolicy policy;
				if (0 != pPolicyFilePath)
				{
					SPAIopWString policyFilePath(pPolicyFilePath);
					SPAIopPolicyFileStreamXML xmlPolicyStream;
					xmlPolicyStream.SetUnstreamFilePath(policyFilePath);

					policy.Deserialize(xmlPolicyStream);
				}

				if (hasOutputFileName)
				{
					if (false == OpenFileForConsoleOutput(pTargetFilePath))
						return 11;
				}

				result = ImportPSModel(pSourceFilePath, policy);
				CloseFileForConsoleOutput();
			}
			else
			{
				PrintHelp();
			}
		}
	}
	catch (const SPAIopAcisError& fatalErr)
	{
		DisplayError(fatalErr);
		result = 1;
	}
	catch (...)
	{
		PrintMessage(L"FAILURE: Unknown exception caught! ");
		result = 1;
	}

	api_stop_modeller();

	return result;
}

//-----------------------------------------------------------------------------------------


